package com.ZeroCarbon.utils;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTValidator;
import cn.hutool.jwt.signers.JWTSigner;
import com.ZeroCarbon.exception.BusinessLogicException;
import com.ZeroCarbon.prefix.RedisKey;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.Date;
import java.util.UUID;

/**
 * Jwt工具类
 *
 * @since 2024-10-15
 */
@Component
@RequiredArgsConstructor
public class JwtTool {

    /**
     * 签名对象
     */
    private final JWTSigner jwtSigner;

    /**
     * redis对象
     */
    private final StringRedisTemplate redis;


    /**
     * 创建Access Token
     *
     * @param userId   用户Id
     * @param request  请求对象
     * @param duration 持续时间
     */
    public String generateAccessToken(String userId, HttpServletRequest request, Duration duration) {
        //获取ip地址
        String ip = request.getHeader("X-Real-IP");
        //获取User-Agent
        String userAgent = request.getHeader("User-Agent");
        System.out.println(ip + " " + userAgent);
        // 获取加密指纹
        String deviceFingerprint = generateDeviceFingerprint(ip, userAgent);
        // 返回Token
        return JWT.create()
                .setPayload("userId", userId)
                .setPayload("deviceFp", deviceFingerprint)
                .setExpiresAt(new Date(System.currentTimeMillis() + duration.toMillis()))
                .setSigner(jwtSigner)
                .sign();
    }

    /**
     * 创建Refresh Token
     *
     * @param userId    用户Id
     * @param ip        发送请求ip
     * @param userAgent User-Agent
     * @param duration  持续时间
     */
    public String generateRefreshToken(String userId, String ip, String userAgent, Duration duration) {
        // 生成指纹
        String deviceFingerprint = generateDeviceFingerprint(ip, userAgent);
        // 生成UUID
        String uuid = UUID.randomUUID().toString();
        // 目的是防止Refresh Token被盗取之后能直接获取Access Token的情况
        redis.opsForValue().set(RedisKey.REFRESH + userId, uuid);
        // 返回Token
        return JWT.create()
                .setPayload("deviceFp", deviceFingerprint)
                .setPayload("secret", uuid)
                .setExpiresAt(new Date(System.currentTimeMillis() + duration.toMillis()))
                .setSigner(jwtSigner)
                .sign();
    }

    /**
     * 生成唯一指纹
     *
     * @param ip        与服务器建立TCP连接的ip地址
     * @param userAgent User-Agent
     */
    private String generateDeviceFingerprint(String ip, String userAgent) {
        // 生成SHA-256哈希值
        return DigestUtils.sha256Hex(ip + userAgent);
    }

    /**
     * 解析Access Token
     *
     * @param token     接收token
     * @param ip        与服务器建立TCP连接的ip地址
     * @param userAgent User-Agent
     */
    public String parseAccessToken(String token, String ip, String userAgent) throws BusinessLogicException {
        if (token == null) {
            throw new BusinessLogicException("用户尚未登录");
        }
        // 获取JWT对象
        JWT jwt;
        // 用当前的请求对象生成指纹
        String currentDeviceFingerprint = generateDeviceFingerprint(ip, userAgent);
        try {
            // 用传入的Token和签名设置JWT对象
            jwt = JWT.of(token).setSigner(jwtSigner);
        } catch (Exception e) {
            throw new BusinessLogicException("用户登录鉴权失效");
        }
        //从Access Token中获取指纹
        String tokenDeviceFingerprint = jwt.getPayload("deviceFp").toString();
        // 如果指纹不相同则直接失败
        if (!currentDeviceFingerprint.equals(tokenDeviceFingerprint)) {
            throw new BusinessLogicException("设备环境变化，请重新登录");
        }
        // 验证token是否有效
        if (!jwt.verify()) {
            throw new BusinessLogicException("用户登录鉴权失效");
        }
        // 验证token是否过期
        try {
            JWTValidator.of(jwt).validateDate();
        } catch (Exception e) {
            throw new BusinessLogicException("账户登录信息过期");
        }
        // 验证数据
        String userId = jwt.getPayload("userId").toString();
        // 判断userId是否为null
        if (userId == null) {
            throw new BusinessLogicException("用户登录鉴权失效");
        }
        return userId;
    }

    /**
     * 解析Refresh Token
     */
    public void parseRefreshToken(String token, HttpServletRequest request) throws BusinessLogicException {
        // 获取用户id
        String userId = request.getHeader("userId");
        // 获取用户ip地址
        String ip = GatewayIpUtils.getClientIp(request);
        // 获取User-Agent
        String userAgent = request.getHeader("User-Agent");
        // 获取JWT对象
        JWT jwt;
        // 用当前的请求对象生成指纹
        String currentDeviceFingerprint = generateDeviceFingerprint(ip, userAgent);
        // 根据用户id查找相关UUID
        String uuid = redis.opsForValue().get(RedisKey.REFRESH + userId);
        try {
            // 设置token和签名
            jwt = JWT.of(token).setSigner(jwtSigner);
            // 验证token是否过期
            try {
                JWTValidator.of(jwt).validateDate();
            } catch (Exception e) {
                throw new BusinessLogicException(BusinessLogicException.EXPIRED_EXCEPTION);
            }
            // 获取token中的指纹
            String tokenDeviceFp = jwt.getPayload("deviceFp").toString();
            // 如果两个指纹不一样则说明环境不一致，需要重新登录
            if (!currentDeviceFingerprint.equals(tokenDeviceFp)) {
                throw new BusinessLogicException("环境异常");
            }
            // 获取token中的UUID
            String tokenUUID = jwt.getPayload("secret").toString();
            // 如果两个UUID不一致则说明用户id发生了变化
            if (!tokenUUID.equals(uuid)) {
                throw new BusinessLogicException("用户身份异常");
            }
        } catch (Exception e) {
            throw new BusinessLogicException(e.getMessage());
        }
    }
}
